package org.example.common.aspect;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.example.exception.NewValidationFailedException;
import org.example.exception.ValidationFailedException;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;

import java.util.ArrayList;
import java.util.List;

import static java.lang.String.format;

/**
 * 在方法调用之前对参数进行校验，并统一返回错误信息
 *
 * @author Hu Jingling
 * @since 2020-08-04
 */
@Slf4j
@Aspect
@Component
public class ValidationAdvice {

    private List<Class<?>> oldVersionControllerList;

    /**
     * 构造方法
     */
    public ValidationAdvice() {
        log.debug("ValidationAdvice empty constructor...");
        if (oldVersionControllerList == null) {
            initOldVersionControllerList();
        }
    }

    /**
     * 初始化修版本controller列表
     */
    private void initOldVersionControllerList() {
        oldVersionControllerList = new ArrayList<>();
    }

    /**
     * 所有 Web 控制器方法
     */
    @Pointcut("execution(* org.example.controller..*Controller.*(..))")
    public void restController() {
        log.debug("进入ValidationAdvice切点restController");
    }

    /**
     * 测试
     *
     * @param bindingResult 绑定
     */
    @Pointcut("args(..,bindingResult)")
    public void withBindingResult(BindingResult bindingResult) {
        log.debug("进入ValidationAdvice切点withBindingResult");
    }

    /**
     * aop测试
     *
     * @param joinPoint     连结点
     * @param bindingResult 绑定
     */
    @Before("restController() && withBindingResult(bindingResult)")
    void handler(JoinPoint joinPoint, BindingResult bindingResult) {

        String className = joinPoint.getTarget().getClass().getSimpleName();
        String signature = joinPoint.getSignature().getName();

        if (bindingResult.hasErrors()) {
            log.debug(format("JSR303 Validation Failed: %s , Method: %s ", className, signature));
            FieldErrorWrapper wrapper = new FieldErrorWrapper(bindingResult);
            log.debug("ValidationFailedException :" + wrapper);
            // 判断切点类是新版还是旧版
            if (oldVersionControllerList.contains(joinPoint.getTarget().getClass())) {
                throw new ValidationFailedException(wrapper);
            } else {
                throw new NewValidationFailedException(wrapper);
            }

        }
    }

}
